'''
The notebook consist basically of a gtk.Notebook filled with many
methods for save and load sheets.
'''

from symbide import ModuleManager, LogManager, PATHS

import os.path, cPickle
try:
    from os.path import samefile
except:
    samefile = lambda x, y: os.path.normpath(x)==os.path.normpath(y)
from os import listdir, remove

from xml.dom import minidom


import gtk
from widgetOrganizer import WidgetOrganizer
from worksheet import Worksheet
from pythonsheet import Pythonsheet
import dialogs
from dialog_export import ExportDialog


class Notebook(WidgetOrganizer, gtk.Notebook):
    '''
    A gtk.Notebook widget that can hold an arbitraire amount of
    sheets. It is responsible of opening, saving, storing,
    restoring and closing them.
    '''
    
    def __init__(self, ancestor):
        gtk.Notebook.__init__(self)
        WidgetOrganizer.__init__(self, 'Notebook', ancestor)
        
        self.connect('page-reordered', self._event_reorder)
        self.connect('event-after', self._event_change)
        self.activeInput = None
        
        #used for search routine
        self.cursor_moved = False
        
        #loads sheets of the last session
        self.restoreSheets()
        
        #if no sheets of the last session could be loaded, create
        #a empty worksheet
        if not self.children:
            self.addWorksheet()
    
    def _event_reorder(self, notebook, sheet, pos):
        '''
        Adapts the order of the children list to the tab order.
        '''
        
        self.children.remove(sheet)
        self.children.insert(pos, sheet)
    
    def _event_change(self, *args):
        self.getAncestor('MainWindow').setTitle()
    
    def _checkCount(self):
        '''
        If less than two sheets are open, the notebook tabs are set invisible.
        '''
        
        if len(self.children)<2:
            self.set_show_tabs(False)
        else:
            self.set_show_tabs(True)
    
    def addWorksheet(self, DOM = None, path = ''):
        '''
        Creates a new worksheet and adds it to the widget tree. If
        a DOM representation is given, it is passed to the worksheet.
        The path defines where this worksheet will be saved on default.
        '''
        
        worksheet = Worksheet(self, DOM, path)
        self.addChild(worksheet, -1)
        self.append_page(worksheet, worksheet.tabWidget)
        
        #focus on the new worksheet
        self.set_current_page(self.page_num(worksheet))
        
        self.set_tab_reorderable(worksheet, True)
        self._checkCount()
        return worksheet
    
    def addPythonsheet(self, text='', path=''):
        '''
        Creates a new pythonsheet and adds it to the widget tree. If
        a string is given, it is passed to the pythonsheet.
        The path defines where this pythonsheet will be saved on default.
        '''
        
        pythonsheet = Pythonsheet(self, text, path)
        self.addChild(pythonsheet, -1)
        self.append_page(pythonsheet, pythonsheet.tabWidget)
        
        #focus on the new pythonsheet
        self.set_current_page(self.page_num(pythonsheet))
        
        self.set_tab_reorderable(pythonsheet, True)
        self._checkCount()
        return pythonsheet
        
    def openSheet(self, path=False, usePath=True):
        '''
        Opens saved sheets from path. If path is not given, the user is
        asked to choose a file in a dialog. It is possible to open more than
        one file at once.
        '''
        
        if not path:
            path = dialogs.open()
        #if the user didn't choose a path it aborts
        if not path:
            return
        #if only one path was given it has to be enclosed in a list
        elif isinstance(path, (str, unicode)):
            path = [path]
        
        #now all files get opened
        s = None
        for p in path:
            s = self.openWorksheet(p, usePath)
            if not s:
                s = self.openPythonsheet(p, usePath)
        return s
    
    def openWorksheet(self, path, usePath=True):
        '''
        Opens a Symbide-xml file, creates it's DOM-representation and
        displays it in a new worksheet.
        '''
        
        try:
            DOM = minidom.parse(path)
        except:
            return False
        #TODO: validate input xml befor parsing
        if usePath:
            p = path
        else:
            p = ''
        childDOM = DOM.getElementsByTagName('worksheet')[0]
        w = self.addWorksheet(childDOM, p)
        return w 
    
    def openPythonsheet(self, path, usePath=True):
        '''
        Opens a textfile and shows this text in a Pythonsheet.
        '''
        
        f = file(path, 'r')
        text = f.read()
        f.close()
        if usePath:
            p = path
        else:
            p = ''
        w = self.addPythonsheet(text, p)
        return w
    
    def closeSheet(self, sheet):
        '''
        Closes a sheet if nothing has changed since the last saving.
        Otherwise it opens a dialog to ask if the user wants to save
        the file. Depending an the answer the sheet is either
        closed, saved, or the closing is aborted. 
        '''
        
        save = None
        if sheet.hasChanged() or not sheet.path:
            save = dialogs.saveSingle(sheet)
        else:
            self.removeChild(sheet)
            self._checkCount()
            return
        
        #save is one of: False, True, None
        if save==False:
            self.removeChild(sheet)
        elif save and self.saveSheet(sheet):
            self.removeChild(sheet)
        self._checkCount()
    
    def saveSheet(self, sheet=None):
        '''
        Saves a sheet to file. If it has not yet a path
        the saveSheetAs method is called.
        
        sheet can be:
            -a worksheet or pythonsheet: this sheet is saved
            -None: the currently active sheet is saved
            -'all': all sheets are saved
        '''
        
        if not sheet:
            pos = self.get_current_page()
            sheet = [self.children[pos]]
        elif type(sheet)==str and sheet=='all':
            sheet = self.children
        else:
            sheet = [sheet]
        
        for s in sheet:
            if not s.path:
                if self.saveSheetAs(s):
                    continue  
                else:
                    return False
            if isinstance(s, Worksheet):
                buf = s.toxml()
            elif isinstance(s, Pythonsheet):
                buf = s.getChildren('Input').get_text()
            else:
                return True
            
            try:
                f = file(s.path, 'w')
                f.write(buf)
                f.close()
                s.setChanged(False)
            except Exception:
                #TODO: give hint that saving failed
                LogManager.alert('Saving failed!')
                return False
        return True
            
    def saveSheetAs(self, sheet=None):
        '''
        Opens Save-Dialog and saves given or currently opened sheet
        to the path choosen by the user.
        sheet can be:
            -a worksheet or pythonsheet: this sheet is saved
            -None: the currently opened sheet is saved
        '''
        
        if not sheet:
            pos = self.get_current_page()
            sheet = self.children[pos]
        
        #TODO: set default-path
        path = dialogs.saveAs(sheet)
        if path:
            if isinstance(sheet, Worksheet):
                buf = sheet.toxml()
            elif isinstance(sheet, Pythonsheet):
                buf = sheet.getChildren('Input').get_text()
            else:
                return True
            try:
                f = file(path, 'w')
                f.write(buf)
                f.close()
            except Exception:
                #TODO: give hint that saving failed
                LogManager.alert('Saving failed!')
                return False
            sheet.setPath(path)
            sheet.setChanged(False)
            return True
        else:
            return False
    
    def saveChangedSheets(self):
        '''
        Opens a dialog where the user can see all changed sheets
        and can choose wich ones are to save. If just one sheet is
        changed, a different dialog appears were the user can just
        choose between yes and no.
        
        This will mostly be used before closing Symbide.
        '''
        
        changedSheets = []
        sheets = False
        
        for sheet in self.children:
            if sheet.hasChanged():
                changedSheets.append(sheet)
        if len(changedSheets)==1:
            sheets = dialogs.saveSingle(changedSheets[0])
        elif len(changedSheets)>1:
            sheets = dialogs.saveMulti(changedSheets)
        
        if sheets==None:
            return False
        elif sheets:
            for s in sheets:
                if not self.saveSheet(s):
                    return False
        return True
        
    def exportSheet(self, sheet=None):
        '''
        Exports the sheet as html document.
        '''
        
        if not sheet:
            pos = self.get_current_page()
            try:
                sheet = self.children[pos]
            except:
                LogManager.alert("Can't find sheet!")
                return 
        
        ExportDialog(sheet)
      
    def storeSheets(self):
        '''
        Stores all currently open sheets, so they can be reopened when
        starting a new session. When a sheet has been saved somewhere
        by the user, only the path is stored. Otherwise it is saved into
        the stored_worksheets folder defined in path.xml.
        '''
        
        #delete previously stored sheets
        self.deleteStoredSheets()
        
        sheets = []
        path = os.path.join(PATHS['stored_worksheets'], 'sheet%s.symb')
        for s in self.children:
            if not s.path:
                s.path = path%len(sheets)
                self.saveSheet(s)
            sheets.append(s.path)
        
        #the sheet list is stored with pickle
        f = open(os.path.join(PATHS['stored_worksheets'], 'sheets.pkl'),'w')
        cPickle.dump(sheets, f)
          
    def restoreSheets(self):
        '''
        Opens all stored sheets. If files can't be found they are
        just ignored.
        '''
        tempDir = PATHS['stored_worksheets']
        
        #try to load infos where sheets are saved
        try:
            f = open(os.path.join(tempDir,'sheets.pkl'))
            oldsheets = cPickle.load(f)
        except:
            oldsheets = []
        
        #get path of all sheets that still exist
        sheets = []
        for s in oldsheets:
            if os.path.exists(s):
                sheets.append(s)
        
        #add sheets to notebook
        for path in sheets:
            if samefile(os.path.dirname(path),PATHS['stored_worksheets']):
                use = False
            else:
                use = True
            self.openSheet(path, use) 

    def deleteStoredSheets(self):
        '''
        Deletes all xml-files in the stored_worksheets directory
        '''
        
        dir = PATHS['stored_worksheets']
        files = listdir(dir)
        for f in files:
            p = os.path.join(dir,f)
            if os.path.isfile(p) and os.path.splitext(f)[1]=='.symb':
                remove(p)
    
    def insertText(self, text):
        '''
        Inserts text into the currently active buffer
        '''
        
        if not self.children:
            self.addWorksheet()
        if self.activeInput:
            self.activeInput.buffer.insert_at_cursor(text)
        
    def insertInput(self, type):
        '''
        Inserts a new input into the currently active buffer
        '''
        
        if not self.children:
            self.addWorksheet()
        if self.activeInput and self.activeInput.identifier=='Input':
            self.activeInput.insertInput(type)
    
    def set_cursor_moved(self, moved=True):
        if moved:
            self.cursor_moved = True
    
    def get_current_sheet(self):
        pos = self.get_current_page()
        if 0<=pos<len(self.children): 
            return self.children[pos]
    
    def execute_current_sheet(self):
        sheet = self.get_current_sheet()
        sheet.call('Input', 'execute')
        